//
// Created by THE13 on 2024/2/14.
//
#include "st7789.h"

/*----------------------------------- private function -----------------------------------*/
//void ST7789_WriteCommand(uint8_t cmd);
static void ST7789_WriteSmallData(uint8_t data);

uint8_t st7789_init_flag = 0;
uint16_t DMA_MIN_SIZE = 10;//dma  min buff

/*----------------------------------- 0. Init st7789 conctrler -----------------------------------*/
void ST7789_Init()
{
    /* Open In blk */
		#ifdef BLK_PORT
    BLK_ON();
		#endif
    /* Sleep In Command */
    ST7789_WriteCommand (ST7789_SLPIN);
    /* Wait for 10ms */
    HAL_Delay(10);
    /* SW Reset Command */
    ST7789_RST_Clr();
    HAL_Delay(25);
    ST7789_RST_Set();
    /* Wait for 200ms */
    HAL_Delay(200);
    /* Sleep Out Command */
    ST7789_WriteCommand (ST7789_SLPOUT);	//	Out of sleep mode
    /* Wait for 120ms */
    HAL_Delay(120);
    /* Normal display for Driver Down side */
    ST7789_WriteCommand (ST7789_NORON);		//	Normal Display on
    /* Color mode 16bits/pixel */
    ST7789_WriteCommand(ST7789_COLMOD);		//	Set color mode
    ST7789_WriteSmallData(ST7789_COLOR_MODE_16bit);//  RGB565 (16bit) RGB565-262K->0x65  RGB565-65K->0x55 //65K色的屏幕较昏暗，262K色的较艳丽
    /* Display inversion Off */
    ST7789_WriteCommand (ST7789_INVON);		//	Inversion OFF反色关
    /*--------------- ST7789H2 Frame rate setting -------------------------------*/
    /* PORCH control setting */
    ST7789_WriteCommand(ST7789_PORCH_CTRL);//	Porch control
    {
        uint8_t data[] = {0x0C, 0x0C, 0x00, 0x33, 0x33};
        ST7789_WriteData(data, sizeof(data));
    }
    ST7789_SetRotation(ST7789_ROTATION);	//	MADCTL (Display Rotation)

    /* Internal LCD Voltage generator settings */
    /* GATE control setting */
    ST7789_WriteCommand(ST7789_GATE_CTRL);  //	Gate Control
    ST7789_WriteSmallData(0x35);			//	Default value
    /* VCOM setting */
    ST7789_WriteCommand(ST7789_VCOM_SET);   //	VCOM setting
    ST7789_WriteSmallData(0x19);			//0.725v (default 0.75v for 0x20)
    /* LCM Control setting */
    ST7789_WriteCommand(ST7789_LCM_CTRL);   //	LCMCTRL
    ST7789_WriteSmallData (0x2C);         //	Default value
    /* VDV and VRH Command Enable */
    ST7789_WriteCommand (ST7789_VDV_VRH_EN);				//	VDV and VRH command Enable
    ST7789_WriteSmallData (0x01);			//	Default value
    ST7789_WriteSmallData (0xFF);			//	Default value
    /* VDV Set */
    ST7789_WriteCommand (ST7789_VDV_SET);				//	VDV set
    ST7789_WriteSmallData (0x20);			//	Default value
    /* VRH Set */
    ST7789_WriteCommand (0xC3);				//	VRH set
    ST7789_WriteSmallData (0x12);			//	+-4.45v (defalut +-4.1v for 0x0B)
    /* Frame Rate Control in normal mode */
    ST7789_WriteCommand (ST7789_FR_CTRL);				//	Frame rate control in normal mode
    ST7789_WriteSmallData (0x01);			//	Default value (119HZ)
    /* Power Control */
    ST7789_WriteCommand (ST7789_POWER_CTRL);				//	Power control
    ST7789_WriteSmallData (0xA4);			//	Default value
    ST7789_WriteSmallData (0xA1);			//	Default value

    ST7789_WriteCommand (0xCC);				//	PWM HZ SET
    ST7789_WriteSmallData (0x28);			//	1428KHZ
    /*--------------- ST7789H2 Gamma setting ------------------------------------*/
    /* Positive Voltage Gamma Control */
    ST7789_WriteCommand(ST7789_PV_GAMMA_CTRL);
    {
        uint8_t data[] = {0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23};//配置1
//	    uint8_t data[] = {0xD0, 0x0A, 0x10, 0x0C, 0x0C, 0x18, 0x35, 0x43, 0x4D, 0x39, 0x13, 0x13, 0x2D, 0x34};//配置2
//	    uint8_t data[] = {0xD0, 0x08, 0x11, 0x08, 0x0C, 0x15, 0x39, 0x33, 0x50, 0x36, 0x13, 0x14, 0x29, 0x2D};
        ST7789_WriteData(data, sizeof(data));
    }
    /* Negative Voltage Gamma Control */
    ST7789_WriteCommand(ST7789_NV_GAMMA_CTRL);
    {
        uint8_t data[] = {0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23};//配置1
//	    uint8_t data[] = {0xD0, 0x05, 0x0B, 0x06, 0x05, 0x02, 0x35, 0x43, 0x4D, 0x16, 0x15, 0x15, 0x2E, 0x32};//配置2
//	    uint8_t data[] = {0xD0, 0x08, 0x10, 0x08, 0x06, 0x06, 0x39, 0x44, 0x51, 0x0B, 0x16, 0x14, 0x2F, 0x31};
        ST7789_WriteData(data, sizeof(data));
    }
    /* Display ON command */
    ST7789_WriteCommand (ST7789_DISPON);	//	Main screen turned on

    /* Tearing Effect Line On: Option (00h:VSYNC Interface OFF, 01h:VSYNC Interface ON) */
    ST7789_WriteCommand (ST7789_TEARING_EFFECT);
    ST7789_WriteSmallData (0X01);


    HAL_Delay(50);
    ST7789_Fill_Color(WHITE);//	Fill with Black.
    HAL_Delay(50);

    ST7789_SetAddressWindow(0, 0, ST7789_WIDTH-1, ST7789_HEIGHT-1);
		HAL_Delay(50);
		
    st7789_init_flag = 1;
}

void ST7789_WriteData_Step1(uint8_t *buff)
{
    ST7789_Select();
    ST7789_DC_Set();

    HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, buff, 64800);
}

void ST7789_WriteData_Step2(uint8_t *buff)
{
    HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, buff+57600, 64800-57600);
}

/*----------------------------------- 1. st7789 write data -----------------------------------*/
void ST7789_WriteData(uint8_t *buff, size_t buff_size)
{
    ST7789_Select();
    ST7789_DC_Set();

    // split data in small chunks because HAL can't send more than 64K at once
    while (buff_size > 0)
    {
        uint16_t chunk_size = buff_size > 65535 ? 65535 : buff_size;
#ifdef USE_DMA
        if (DMA_MIN_SIZE <= buff_size)
			{
				HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, buff, chunk_size);
				while (ST7789_SPI_PORT.hdmatx->State != HAL_DMA_STATE_READY)
				{}
			}
			else
				HAL_SPI_Transmit(&ST7789_SPI_PORT, buff, chunk_size, HAL_MAX_DELAY);
#else
        HAL_SPI_Transmit(&ST7789_SPI_PORT, buff, chunk_size, HAL_MAX_DELAY);
#endif
        buff += chunk_size;
        buff_size -= chunk_size;
    }
//		while (ST7789_SPI_PORT.hdmatx->State != HAL_DMA_STATE_READY)
//		{
//		}
    ST7789_UnSelect();
}

/*----------------------------------- 2. Set the rotation direction of the display ------------------------------*/
void ST7789_SetRotation(uint8_t m)
{
    ST7789_WriteCommand(ST7789_MADCTL);	// MADCTL
    switch (m) {
        case 0:
            ST7789_WriteSmallData(ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB);
            break;
        case 1:
            ST7789_WriteSmallData(ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
            break;
        case 2:
            ST7789_WriteSmallData(ST7789_MADCTL_RGB);
            break;
        case 3:
            ST7789_WriteSmallData(ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
            break;
        default:
            break;
    }
}


/*----------------------------------- 3. Fill the DisplayWindow with single color ------------------------------*/
/**
 * @brief Fill the DisplayWindow with single color
 * @param color -> color to Fill with
 * @return none
 */
void ST7789_Fill_Color(uint16_t color)
{
    uint16_t i,j,num;
    ST7789_SetAddressWindow(0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
    ST7789_Select();

#ifdef USE_DMA

    for (i = 0; i < (ST7789_HEIGHT / HOR_LEN); i++)//需要这么多次才能铺满
		{
			memset(disp_buf, color, sizeof(disp_buf));
			ST7789_WriteData((uint8_t *)disp_buf, sizeof(disp_buf));
			j++;
		}
		num = ST7789_HEIGHT-(ST7789_HEIGHT/HOR_LEN)*j;
		if(num!=0)
		{
			memset(disp_buf, color, num);
			ST7789_WriteData((uint8_t *)disp_buf, num);
		}
#else
    uint16_t j;
    for (i = 0; i < ST7789_WIDTH; i++)
        for (j = 0; j < ST7789_HEIGHT; j++) {
            uint8_t data[] = {color >> 8, color & 0xFF};
            ST7789_WriteData(data, sizeof(data));
        }
#endif
    ST7789_UnSelect();
}

/*----------------------------------- 4.Set address of DisplayWindow -----------------------------------*/
void ST7789_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    ST7789_Select();
    uint16_t x_start = x0 + X_SHIFT, x_end = x1 + X_SHIFT;
    uint16_t y_start = y0 + Y_SHIFT, y_end = y1 + Y_SHIFT;

    /* Column Address set */
    ST7789_WriteCommand(ST7789_CASET);
    {
        uint8_t data[] = {(uint8_t)(x_start >> 8), (uint8_t)(x_start & 0xFF), (uint8_t)(x_end >> 8), (uint8_t)(x_end & 0xFF)};
        ST7789_WriteData(data, sizeof(data));
    }

    /* Row Address set */
    ST7789_WriteCommand(ST7789_RASET);
    {
        uint8_t data[] = {(uint8_t)(y_start >> 8), (uint8_t)(y_start & 0xFF), (uint8_t)(y_end >> 8), (uint8_t)(y_end & 0xFF)};
        ST7789_WriteData(data, sizeof(data));
    }
    /* Write to RAM */
    ST7789_WriteCommand(ST7789_RAMWR);
    ST7789_UnSelect();
}

/*----------------------------------- base. private function -----------------------------------*/
/* Write command to ST7789 controller ------------------------------*/
void ST7789_WriteCommand(uint8_t cmd)
{
    ST7789_Select();
    ST7789_DC_Clr();
    HAL_SPI_Transmit(&ST7789_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY);
    ST7789_UnSelect();
}

/**
 * @brief Write data to ST7789 controller, simplify for 8bit data.
 * data -> data to write
 * @return none
 */
static void ST7789_WriteSmallData(uint8_t data)
{
    ST7789_Select();
    ST7789_DC_Set();
    HAL_SPI_Transmit(&ST7789_SPI_PORT, &data, sizeof(data), HAL_MAX_DELAY);
    ST7789_UnSelect();
}
